package com.geeguo.ebuilder.core.security.service;

import com.geeguo.ebuilder.core.common.utils.GsonUtils;
import com.geeguo.ebuilder.core.common.utils.IpUtils;
import com.geeguo.ebuilder.core.common.utils.ServletUtils;
import com.geeguo.ebuilder.core.common.utils.SpringUtils;
import com.geeguo.ebuilder.core.common.utils.key.UUIDGenerator;
import com.geeguo.ebuilder.core.redis.service.RedisCacheService;
import com.geeguo.ebuilder.core.redis.utils.AbstractCacheKeyFactory;
import com.geeguo.ebuilder.core.security.config.LoginConfig;
import com.geeguo.ebuilder.core.security.constants.SecurityConstants;
import com.geeguo.ebuilder.core.security.model.BaseLoginUser;
import com.geeguo.ebuilder.core.security.utils.JwtUtils;
import com.geeguo.ebuilder.core.security.utils.TokenHelper;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

@Slf4j
public class TokenService<T extends BaseLoginUser> {

    private final LoginConfig loginConfig = SpringUtils.getBean(LoginConfig.class);
    private final RedisCacheService redisCacheService = SpringUtils.getBean(RedisCacheService.class);
    private final AbstractCacheKeyFactory cacheKeyFactory = SpringUtils.getBean(AbstractCacheKeyFactory.class);

    private final Class<T> loginUserType;

    public TokenService(Class<T> loginUserType) {
        this.loginUserType = loginUserType;
    }

    /**
     * 创建令牌并缓存
     */
    public void createToken(T loginUser) {
        String loginKey = UUIDGenerator.getId();
        String userId = loginUser.getUserId();
        String token = JwtUtils.createToken(getClaimsMap(loginKey, userId));

        loginUser.setLoginKey(loginKey);
        loginUser.setLoginIp(IpUtils.getIpAddr());
        loginUser.setToken(token);
        refreshExpireTime(loginUser);
    }

    /**
     * 获取用户登录信息
     *
     * @return 用户登录信息
     */
    public T getLoginUser() {
        return getLoginUser(ServletUtils.getRequest());
    }

    /**
     * 获取用户登录信息
     *
     * @return 用户登录信息
     */
    public T getLoginUser(HttpServletRequest request) {
        String token = TokenHelper.getToken(request);
        return getLoginUser(token);
    }

    /**
     * 获取用户登录信息
     *
     * @return 用户登录信息
     */
    public T getLoginUser(String token) {
        T loginUser = null;
        try {
            if (StringUtils.isNotEmpty(token)) {
                String loginKey = JwtUtils.getLoginKey(token);
                String loginUserKey = cacheKeyFactory.getLoginUser(loginKey);
                loginUser = GsonUtils.fromJson(redisCacheService.get(loginUserKey), loginUserType);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
        return loginUser;
    }

    /**
     * 删除缓存用户登录信息，删除即注销，Token不再有效
     */
    public void removeLoginUser(HttpServletRequest request) {
        String token = TokenHelper.getToken(request);
        removeLoginUser(token);
    }

    /**
     * 删除缓存用户登录信息，删除即注销，Token不再有效
     */
    public void removeLoginUser(String token) {
        if (StringUtils.isNotEmpty(token)) {
            String loginKey = JwtUtils.getLoginKey(token);
            redisCacheService.del(cacheKeyFactory.getLoginUser(loginKey));
        }
    }

    /**
     * 验证令牌有效期，令牌有效期剩余的时间不足设置的刷新时间，自动刷新缓存
     */
    public void verifyExpireTime(T loginUser) {
        long expireTime = loginUser.getExpireTime();
        long currentTime = System.currentTimeMillis();
        if (expireTime - currentTime < (long) loginConfig.getTokenRefreshTime() * 60 * 1000) {
            refreshExpireTime(loginUser);
        }
    }

    /**
     * 刷新令牌有效期，同时刷新登录用户信息中的登录时间、过期时间和缓存过期时间
     */
    public void refreshExpireTime(T loginUser) {
        loginUser.setLoginTime(System.currentTimeMillis());
        loginUser.setExpireTime(loginUser.getLoginTime() + (long) loginConfig.getTokenExpireTime() * 60 * 1000);

        String loginUserKey = cacheKeyFactory.getLoginUser(loginUser.getLoginKey());
        redisCacheService.set(loginUserKey, GsonUtils.toJson(loginUser), (long) loginConfig.getTokenExpireTime() * 60);
    }

    private Map<String, String> getClaimsMap(String loginKey, String userId) {
        Map<String, String> claimsMap = new HashMap<>();
        claimsMap.put(SecurityConstants.LOGIN_KEY, loginKey);
        claimsMap.put(SecurityConstants.USER_ID, userId);
        return claimsMap;
    }
}